package com.frank.http.expand;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;

import android.content.Context;

/**
 * 本地参数加密
 * 
 * @author Frank
 * @version 1.0 Create by 2016.04.06
 */
public class LocalEncryption {
	/**
	 * 本地参数加密路径
	 * 
	 * @return
	 */
	public static File getEncrptionPath(Context context) {
		File dcp = null;
		File file = null;

		dcp = new File(context.getFilesDir().getAbsolutePath() + File.separator
				+ "frank");
		if (!dcp.exists()) {
			dcp.mkdirs();
			return dcp;
		}

		file = new File(dcp, "key.obj");

		if (!file.exists()) {
			try {
				file.createNewFile();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		return file;
	}

	/**
	 * @Description 加密的时候要使用的密钥，这个方法就是生成一个密钥并保存在文件中
	 *              这个每次生成的密钥都不同，所以这个生成一次就行了，在写入文件的时候应该判断这个文件是否存在，这样是否更合理。
	 */
	public static void createKey(Context context) {
		ObjectOutputStream objectOutputStream = null;
		KeyGenerator keyGenerator = null;
		SecretKey secretKey = null;
		DESKeySpec desKeySpec = null;
		Key convertSecretKey = null;
		byte[] bytesKey = null;
		SecretKeyFactory factory = null;
		try {
			// 生成KEY
			keyGenerator = KeyGenerator.getInstance("DES");
			keyGenerator.init(56);
			secretKey = keyGenerator.generateKey();
			bytesKey = secretKey.getEncoded();

			// KEY转换
			try {
				desKeySpec = new DESKeySpec(bytesKey);
			} catch (InvalidKeyException e) {
				e.printStackTrace();
			}
			factory = SecretKeyFactory.getInstance("DES");
			try {
				convertSecretKey = factory.generateSecret(desKeySpec);
			} catch (InvalidKeySpecException e) {
				e.printStackTrace();
			}
			File file = getEncrptionPath(context);
			if (file != null && file.length() < 1) {
				// 将生成的密钥对象写入文件。
				objectOutputStream = new ObjectOutputStream(
						new FileOutputStream(file));
				objectOutputStream.writeObject(convertSecretKey);
			}

		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (objectOutputStream != null) {
				try {
					objectOutputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * @param file
	 *            密钥Key对象的路径。注意使用该方法的时候，确保你已经生成了密钥。
	 * @return
	 * @Description 从文件中读出Key,用于加密使用。
	 */
	public static Key getKey(File file) {
		Key key = null;
		ObjectInputStream objectInputStream = null;
		try {
			// 将生成的密钥对象从文件中读取出来，然后再强制转换成一个密钥对象。
			objectInputStream = new ObjectInputStream(new FileInputStream(file));
			key = (Key) objectInputStream.readObject();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} finally {
			if (objectInputStream != null) {
				try {
					objectInputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return key;
	}

	/**
	 * @param source
	 * @return 放回一个byte数组，为什么不放回字符串，是因为解密的时候要传入这个byte数组才能进行解密，如果解密的时候传入的是字符串
	 *         那么就会出错，愿意是编码的问题。
	 * @Description 将传入的字符串进行加密 下面写了将这种byte数组转换成字符串的方法。直接在调用就行了。
	 */
	public static byte[] encrypt(String source, Context context) {
		byte[] target = null;
		try {
			byte[] center = source.getBytes();
			Key key = getKey(getEncrptionPath(context));
			Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");

			cipher.init(Cipher.ENCRYPT_MODE, key);

			target = cipher.doFinal(center);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			e.printStackTrace();
		} catch (IllegalBlockSizeException e) {
			e.printStackTrace();
		} catch (BadPaddingException e) {
			e.printStackTrace();
		}

		return target;
	}

	/**
	 * @param source
	 *            加密后的byte数组。可用加密方法encrypt（“String”）生成即可
	 * @return 解密后的字符串。
	 * @Description 解密算法。
	 */
	public static byte[] decrypt(byte[] source, Context context) {
		byte[] dissect = null;
		try {
			Key key = getKey(getEncrptionPath(context));
			Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
			cipher.init(Cipher.DECRYPT_MODE, key);// 使用私钥解密
			dissect = cipher.doFinal(source);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			e.printStackTrace();
		} catch (IllegalBlockSizeException e) {
			e.printStackTrace();
		} catch (BadPaddingException e) {
			e.printStackTrace();
		}
		return dissect;
	}

	/**
	 * 异或加密和解密算法
	 * 
	 * @param is
	 * @param os
	 * @throws Exception
	 */
	public static void encodeAndDecode(InputStream is, OutputStream os)
			throws Exception {
		int bytes = -1;
		while ((bytes = is.read()) != -1) {
			bytes = bytes ^ 0xff;// 和0xff进行异或处理
			os.write(bytes);
		}
	}

	/**
	 * 加密
	 * 
	 * @param content
	 * @param context
	 * @return
	 * @throws Exception
	 */
	public static byte[] encodeContent(String content, Context context)
			throws Exception {
		InputStream is = null;
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		try {
			is = new ByteArrayInputStream(content.getBytes("utf-8"));
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		try {
			encodeAndDecode(is, out);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return out.toByteArray();
	}

	/**
	 * 解密
	 * 
	 * @param content
	 * @param context
	 * @return
	 * @throws Exception
	 */
	public static String decodeContent(byte[] buff) throws Exception {
		InputStream is = new ByteArrayInputStream(buff);
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		try {
			encodeAndDecode(is, out);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return new String(out.toByteArray(), "utf-8");
	}

	/**
	 * 加密或者解密
	 * 
	 * @param buff
	 * @return
	 * @throws Exception
	 */
	public static byte[] encodeAndDecodeContent(byte[] buff) throws Exception {
		InputStream is = new ByteArrayInputStream(buff);
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		try {
			encodeAndDecode(is, out);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return out.toByteArray();
	}

}
